package com.limedia.explorer.http;


import java.io.IOException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Map.Entry;
import java.util.concurrent.TimeUnit;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import android.annotation.SuppressLint;
import android.content.Context;
import android.util.Log;

import com.limedia.explorer.constant.Constant;
import com.limedia.explorer.entity.HttpParams;
import com.squareup.okhttp.Callback;
import com.squareup.okhttp.Headers;
import com.squareup.okhttp.MediaType;
import com.squareup.okhttp.MultipartBuilder;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.Request.Builder;
import com.squareup.okhttp.RequestBody;
import com.squareup.okhttp.Response;


public class HttpClient {
	
	private static final String TAG = HttpClient.class.getSimpleName();
	private static HttpClient mUploadManager = null;
	private OkHttpClient mOkHttpClient = null;
//	private Context mContext = null;

	public static synchronized HttpClient getInstance(Context context) {
		if(null == mUploadManager) {
			mUploadManager = new HttpClient(context);
		}
		return mUploadManager;
	}
	
	private HttpClient(Context context) {
		super();
//		this.mContext = context;
		this.init();
	}
	
	private void init() {
		this.mOkHttpClient = new OkHttpClient();
		this.mOkHttpClient.setConnectTimeout(Constant.timeOut, TimeUnit.SECONDS);
		this.mOkHttpClient.setWriteTimeout(Constant.timeOut, TimeUnit.SECONDS);
		this.mOkHttpClient.setReadTimeout(Constant.timeOut, TimeUnit.SECONDS);
//		this.mOkHttpClient.setCookieHandler(new CookieManager(new HttpCookieStore(this.mContext), CookiePolicy.ACCEPT_ALL));
//		this.mOkHttpClient.setCookieHandler(new CookieManager(new PersistentCookieStore(this.mContext), CookiePolicy.ACCEPT_ALL));
	}
	
	/**
	 * Asynchronous "get" request
	 * @param url
	 * @param callback
	 * @param tag 
	 */
	private void getAsynInner(String url, ResultCallback callback, HttpParams header, Object tag) {
		Builder builder = new Request.Builder().url(url);
    	if(null != header) {
    		for(Entry<String, String> entry : header.getKeyValueMap().entrySet()) {
    			builder.addHeader(entry.getKey(), entry.getValue());
    		}
    	}
		Request request = builder.tag(tag).build();
		deliveryResult(callback, request);
	}

	/**
	 * Asynchronous "post" request
     * @param url
     * @param callback
     * @param file
     * @param fileKey
     * @param params
     * @throws IOException
     */
    private void postAsynInner(String url, ResultCallback callback, HttpParams header, HttpParams param, Object tag) throws IOException {
        Request request = this.buildMultipartFormRequest(url, header, param, tag);
        Log.e(TAG, request.toString());
        this.deliveryResult(callback, request);
    }
    
    private void postAsynInner(String url, ResultCallback callback, HttpParams header, String param, Object tag) throws IOException {
    	Request request = this.buildEncodingBuilderFormRequest(url, header, param, tag);
        Log.e(TAG, request.toString());
        this.deliveryResult(callback, request);
    }
    
    private void putAsynInner(String url, ResultCallback callback, HttpParams header, String param, Object tag) throws IOException {
    	Request request = this.buildEncodingBuilderFormRequestPut(url, header, param, tag);
        Log.e(TAG, request.toString());
        this.deliveryResult(callback, request);
    }

	private void cancelResquest(Object tag) {
    	this.mOkHttpClient.cancel(tag);
    }
    
    private void postHttpsSetSSL() {
		mOkHttpClient.setSslSocketFactory(getDefaultSSL().getSocketFactory());
    	mOkHttpClient.setHostnameVerifier(DO_NOT_VERIFY);
	}
    
    private Request buildEncodingBuilderFormRequest(String url, HttpParams header, String jsonString, Object tag) {
    	Log.i(TAG, "jsonString is " + jsonString);
    	Builder builder = new Request.Builder().url(url);
    	if(null != jsonString && !"".equals(jsonString)) {
//    		RequestBody requestBody = RequestBody.create(MediaType.parse("application/x-www-form-urlencoded"), jsonString);
    		RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), jsonString);
    		builder.post(requestBody);
    	}
    	
    	if(null != header) {
    		for(Entry<String, String> entry : header.getKeyValueMap().entrySet()) {
    			builder.addHeader(entry.getKey(), entry.getValue());
    		}
    	}
    	return builder.tag(tag).build();
    }
    
    private Request buildEncodingBuilderFormRequestPut(String url, HttpParams header, String jsonString, Object tag) {
    	Log.i(TAG, "jsonString is " + jsonString);
    	Builder builder = new Request.Builder().url(url);
    	if(null != jsonString && !"".equals(jsonString)) {
//    		RequestBody requestBody = RequestBody.create(MediaType.parse("application/x-www-form-urlencoded"), jsonString);
    		RequestBody requestBody = RequestBody.create(MediaType.parse("application/json"), jsonString);
    		builder.put(requestBody);
    	}
    	
    	if(null != header) {
    		for(Entry<String, String> entry : header.getKeyValueMap().entrySet()) {
    			builder.addHeader(entry.getKey(), entry.getValue());
    		}
    	}
    	return builder.tag(tag).build();
    }
    
    private Request buildMultipartFormRequest(String url, HttpParams header, HttpParams params, Object tag) {
		MultipartBuilder builder = new MultipartBuilder().type(MultipartBuilder.FORM);
		for(HttpParams.Param p : params.getParams()) {
			builder.addPart(Headers.of("Content-Disposition",
					"form-data; name=\"" + p.getKey() + "\""),
					RequestBody.create(p.getType(), p.getValue()));
		}
		Log.e(TAG, builder.toString());
		RequestBody requestBody = builder.build();
		Builder requestBuilder = new Request.Builder().url(url);
		if(null != header) {
			requestBuilder.headers(Headers.of(header.getKeyValueMap()));
		}
		requestBuilder.post(requestBody).tag(tag);
		return requestBuilder.build();
	}
    
    private void deliveryResult(final ResultCallback callback, final Request request) {
        mOkHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Request req, final IOException e) {
            	callback.onError(request, e);
            	Log.e(TAG, "onFailure");
            }

            @Override
            public void onResponse(final Response response) {
            	Log.e(TAG, "onResponse");
            	if(null == response) {
            		Log.e(TAG, "response is null");
//            	} else if(!response.isSuccessful()) {
            	} else if(200 != response.code()) {
            		Log.e(TAG, "response.isSuccessful() is false");
            		Log.e(TAG, "response code is " + response.code());
            	} else {
            		Log.e(TAG, "response.isSuccessful() is true");
            	}
            	
            	if(null != response && 200 == response.code()) {
            		callback.onResponse(response);
            	} else {
            		callback.onError(request, new Exception("onResponse Error"));
            	}
            }
        });
    }

	private final static HostnameVerifier DO_NOT_VERIFY = new HostnameVerifier() {
		
		@Override
		public boolean verify(String arg0, SSLSession arg1) {
			return true;
		}
	};
	
	@SuppressLint("TrulyRandom")
	private SSLContext getDefaultSSL() {
		SSLContext sc = null;
		TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
			
			@Override
			public X509Certificate[] getAcceptedIssuers() {
				// TODO Auto-generated method stub
				return null;
			}
			
			@Override
			public void checkServerTrusted(X509Certificate[] chain, String authType)
					throws CertificateException {
				// TODO Auto-generated method stub
			}
			
			@Override
			public void checkClientTrusted(X509Certificate[] chain, String authType)
					throws CertificateException {
				// TODO Auto-generated method stub
			}
		}};
		
		try {
			sc = SSLContext.getInstance("TLS");
			sc.init(null, trustAllCerts, new java.security.SecureRandom());
//			HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
		} catch (Exception e) {
			e.printStackTrace();
		}
		return sc;
	}
	
	public static void getAsyn(Context context, String url, ResultCallback callback, HttpParams header, Object tag) throws IOException {
		getInstance(context).postHttpsSetSSL();
		getInstance(context).getAsynInner(url, callback, header, tag);
	}
    
    public static void postAsyn(
    		Context context, String url, ResultCallback callback, HttpParams header, HttpParams param, Object tag) throws IOException {
		getInstance(context).postAsynInner(url, callback, header, param, tag);
    }
    
    public static void postAsyn(
    		Context context, String url, ResultCallback callback, HttpParams header, String json, Object tag) throws IOException {
		getInstance(context).postAsynInner(url, callback, header, json, tag);
    }
    
    public static void putAsyn(Context context, String url, ResultCallback callback, HttpParams header, String json, Object tag){
    	try {
    		getInstance(context).postHttpsSetSSL();
			getInstance(context).putAsynInner(url, callback, header, json, tag);
		} catch (IOException e) {
			e.printStackTrace();
		}
    }
    
    public static void postAsynHttps(
    		Context context, String url, ResultCallback callback, HttpParams header, HttpParams param, Object tag) throws IOException {
    	getInstance(context).postHttpsSetSSL();
		getInstance(context).postAsynInner(url, callback, header, param, tag);
    }
    
    public static void postAsynHttps(
    		Context context, String url, ResultCallback callback, HttpParams header, String json, Object tag) throws IOException {
    	getInstance(context).postHttpsSetSSL();
    	getInstance(context).postAsynInner(url, callback, header, json, tag);
    }
    
    public static void cancelRequest(Context context, Object tag) {
    	getInstance(context).cancelResquest(tag);
    }

	public static interface ResultCallback {
       public abstract void onError(Request request, Exception e);
       public abstract void onResponse(Response response);
    }
}
